<?php

// @formatter:off
/**
 * @file CTRepositories.php
 * @author Alejandro Dario Simi
 * @date $Date: 2014-05-04 01:33:52 +0000 (Sun, 04 May 2014) $
 *
 * $Id: CTRepositories.php 111 2014-05-04 01:33:52Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/trunk/extensions/Repositories/includes/CTRepositories.php $
 */
class CTRepositories extends WCCronTool {
	/**
	 * @var WCDirectory
	 */
	protected $_preRepo = false;
	/**
	 * @var WCDirectory
	 */
	protected $_repo = false;
	//
	// Public methods.
	public function run($spacer = "") {
		global $wcOptions;

		$groupId = $wcOptions->activated("Group") ? $wcOptions->value("Group") : false;
		$onlyComics = $wcOptions->activated("OnlyComics");
		$onlyImages = $wcOptions->activated("OnlyImages");
		if(!$onlyComics && !$onlyImages) {
			$onlyComics = true;
			$onlyImages = true;
		}

		foreach(WCGroupsHolder::I()->items() as $group) {
			//
			// Ignoring not matching groups when there's a specified
			// one.
			if($groupId && $group->id != $groupId) {
				continue;
			}

			if(is_readable($group->root)) {
				echo "{$spacer}Checking group '{$group}':\n";

				//
				// Loading Repo directory.
				$dirs = WCDirectoriesHolder::I()->itemByNameInGroup(WC_E_REPOSITORIES_MAINDIR, $group);
				$dir = array_shift($dirs);
				if($dir && $dir->ok()) {
					$this->_repo = $dir;
				} else {
					$this->_repo = false;
				}

				if($onlyComics || $onlyImages) {
					//
					// Loading PreRepo directory.
					$dirs = WCDirectoriesHolder::I()->itemByNameInGroup(WC_E_REPOSITORIES_PREDIR, $group);
					$dir = array_shift($dirs);
					if($dir && $dir->ok()) {
						$this->_preRepo = $dir;

						$this->loadPreRepo($group, "{$spacer}\t");
						$this->cleanPreRepo($group, "{$spacer}\t");
						$this->movePreRepo($group, "{$spacer}\t");
					} else {
						echo WCColorText::ShellYellow("{$spacer}\tWarning:")."\n{$spacer}\t\tThere's no directory called '".WC_E_REPOSITORIES_PREDIR."'. PreRepo mechanism disabled\n";
						$this->_preRepo = false;
					}
				}

				echo "{$spacer}\tChecking balance:\n";
				$tries = 1;
				$unbalance = true;
				while($unbalance && $tries <= 5) {
					if($onlyComics) {
						$repo = WCEComicsRepository::Instance($group);
						$repo->reload();
						if(!$repo->broken()) {
							if(!$repo->balanced()) {
								echo "{$spacer}\t\tRepository Status (comics): ".WCColorText::ShellYellow("Unbalanced")." (try: {$tries})\n";
								$unbalance = $this->balanceComics($spacer, $repo);
							} else {
								echo "{$spacer}\t\tRepository Status (comics): ".WCColorText::ShellGreen("Balanced")."\n";
								$unbalance = false;
							}
						} else {
							echo "{$spacer}\t\tRepository Status (comics): ".WCColorText::ShellRed("Broken")."\n";
							$unbalance = false;
						}
					}
					if($onlyImages) {
						$repo = WCEImagesRepository::Instance($group);
						$repo->reload();
						if(!$repo->broken()) {
							if(!$repo->balanced()) {
								echo "{$spacer}\t\tRepository Status (images): ".WCColorText::ShellYellow("Unbalanced")." (try: {$tries})\n";
								$unbalance = $this->balanceImages($spacer, $repo);
							} else {
								echo "{$spacer}\t\tRepository Status (images): ".WCColorText::ShellGreen("Balanced")."\n";
								$unbalance = false;
							}
						} else {
							echo "{$spacer}\t\tRepository Status (images): ".WCColorText::ShellRed("Broken")."\n";
							$unbalance = false;
						}
					}
					$tries++;
				}
			}
		}
	}
	public function showHelp($spacer = "") {
		echo "{$spacer}This tool balance 'WCERepository' directories.\n\n";
		echo "{$spacer}Options:\n";
		echo "{$spacer}\t-c, --only-comics\n";
		echo "{$spacer}\t\tIt only run for comics.\n\n";
		echo "{$spacer}\t-g group-id, --group group-id\n";
		echo "{$spacer}\t\tIt only run active tags for an specific group.\n\n";
		echo "{$spacer}\t-i, --only-images\n";
		echo "{$spacer}\t\tIt only run for images.\n\n";
	}
	//
	// Protected methods.
	protected function balanceComics($spacer, WCEComicsRepository &$repo) {
		$success = true;
		//
		// Detecting destination directory.
		$toDir = $repo->balancer();
		//
		// Detecting unbalanced directory.
		$fromDir = $repo->badDirectory();

		$requredCount = $repo->correctionSize();

		if($fromDir !== false && $toDir !== false) {
			$offset = $fromDir->count - $requredCount;

			if(($toDir->count + $offset) > WC_E_REPOSITORIES_MAX) {
				$offset = WC_E_REPOSITORIES_MAX - $toDir->count;
			}
			echo "{$spacer}\t\tUnbalanced directory: '{$fromDir->name}' ({$fromDir->id})\n";
			echo "{$spacer}\t\tBalancer directory:   '{$toDir->name}' ({$toDir->id})\n";
			echo "{$spacer}\t\tOffset:               '{$offset}'\n";

			$nomads = $this->getNomadsComics($fromDir, $offset);
			foreach($nomads->items() as $comic) {
				echo "{$spacer}\t\tMoving '{$comic->name}' ({$comic->id}) from '{$comic->directory()->name}' ({$comic->directory}) to directory '{$toDir->name}' ({$toDir->id})";
				$futurePath = WCSanitizer::SystemPath("{$toDir->fullPath()}/{$comic->name}");
				if(!file_exists($futurePath)) {
					if($comic->setDirectory($toDir)) {
						echo WCColorText::ShellGreen(" Done")."\n";
					} else {
						echo WCColorText::ShellRed(" Failed")."\n";
					}
				} else {
					echo WCColorText::ShellRed(" Failed")." ('{$futurePath}' already exists)\n";
				}
			}
		} elseif($fromDir === false) {
			echo "{$spacer}\t\t".WCColorText::ShellRed("Unable to get unbalanced directory")."\n";
			$success = false;
		} elseif($toDir === false) {
			if(!$repo->broken() && !$repo->balanced() && !$repo->balancer()) {
				echo "{$spacer}\t\tCreating new balancer directory\n";
				$success = $repo->createBalancer();
			} else {
				echo "{$spacer}\t\t".WCColorText::ShellRed("Unable to get balancer directory")."\n";
				$success = false;
			}
		}

		return $success;
	}
	protected function balanceImages($spacer, WCEImagesRepository &$repo) {
		$success = true;
		//
		// Detecting destination directory.
		$toDir = $repo->balancer();
		//
		// Detecting unbalanced directory.
		$fromDir = $repo->badDirectory();

		$requredCount = $repo->correctionSize();

		if($fromDir !== false && $toDir !== false) {
			$offset = $fromDir->icount - $requredCount;

			if(($toDir->icount + $offset) > WC_E_REPOSITORIES_IMAX) {
				$offset = WC_E_REPOSITORIES_IMAX - $toDir->icount;
			}
			echo "{$spacer}\t\tUnbalanced directory: '{$fromDir->name}' ({$fromDir->id})\n";
			echo "{$spacer}\t\tBalancer directory:   '{$toDir->name}' ({$toDir->id})\n";
			echo "{$spacer}\t\tOffset:               '{$offset}'\n";

			$nomads = $this->getNomadsImages($fromDir, $offset);
			foreach($nomads->items() as $image) {
				echo "{$spacer}\t\tMoving '{$image->name}' ({$image->id}) from '{$image->directory()->name}' ({$image->directory}) to directory '{$toDir->name}' ({$toDir->id})";
				$futurePath = WCSanitizer::SystemPath("{$toDir->fullPath()}/{$image->name}");
				if(!file_exists($futurePath)) {
					if($image->setDirectory($toDir)) {
						echo WCColorText::ShellGreen(" Done")."\n";
					} else {
						echo WCColorText::ShellRed(" Failed")."\n";
					}
				} else {
					echo WCColorText::ShellRed(" Failed")." ('{$futurePath}' already exists)\n";
				}
			}
		} elseif($fromDir === false) {
			echo "{$spacer}\t\t".WCColorText::ShellRed("Unable to get unbalanced directory")."\n";
			$success = false;
		} elseif($toDir === false) {
			if(!$repo->broken() && !$repo->balanced() && !$repo->balancer()) {
				echo "{$spacer}\t\tCreating new balancer directory\n";
				$success = $repo->createBalancer();
			} else {
				echo "{$spacer}\t\t".WCColorText::ShellRed("Unable to get balancer directory")."\n";
				$success = false;
			}
		}

		return $success;
	}
	protected function cleanPreRepo(WCGroup &$group, $spacer) {
		if($this->_repo && $this->_preRepo) {
			echo "{$spacer}Cleaning moved/dead items in '{$this->_preRepo}': ";
			//
			// @{
			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__;
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "delete from {$this->_dbprefix}prerepo\n";
				$query.= "where       pre_group = :group\n";
				$query.= " and        (pre_group, pre_type, pre_id) not in (\n";
				$query.= "            select  img_group,\n";
				$query.= "                    'IM',\n";
				$query.= "                    img_id\n";
				$query.= "            from    {$this->_dbprefix}images\n";
				$query.= "            where   img_group     = :group\n";
				$query.= "             and    img_directory = :directory\n";
				$query.= "             and    img_lost      = 'N'\n";
				$query.= "            union all\n";
				$query.= "            select  cmc_group,\n";
				$query.= "                    'CX',\n";
				$query.= "                    cmc_id\n";
				$query.= "            from    {$this->_dbprefix}comics\n";
				$query.= "            where   cmc_group     = :group\n";
				$query.= "             and    cmc_directory = :directory\n";
				$query.= "             and    cmc_lost      = 'N'\n";
				$query.= "            )\n";

				$stmt = $this->_db->prepare($stmtId, $query);
			}
			// @}

			$stmt->execute(array(
				":group" => $group->id,
				":directory" => $this->_preRepo->id
			));
			echo WCColorText::ShellGreen("{$stmt->rowCount()}\n");
		}
	}
	/**
	 * @param WCDirectory $fromDir
	 * @param int $offset
	 * @return WCGContainer
	 */
	protected function getNomadsComics(WCDirectory &$fromDir, $offset) {
		$container = new WCContainer(WCComicsHolder::I());

		$stmt = null;
		$stmtID = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtID)) {
			$stmt = $this->_db->get($stmtID);
		} else {
			$query = "select  cmc_id\n";
			$query .= "from    {$this->_dbprefix}comics\n";
			$query .= "where   cmc_group     = :group\n";
			$query .= " and    cmc_directory = :directory\n";
			$query .= " and    cmc_lost      = 'N'\n";
			$query .= "order by cmc_added asc\n";

			$stmt = $this->_db->prepare($stmtID, $query);
		}

		$params = array(
			":group" => $fromDir->group()->id,
			":directory" => $fromDir->id
		);
		if($stmt->execute($params)) {
			foreach($stmt->fetchAll() as $row) {
				$container->addId($row["cmc_id"]);
				$offset--;
				if(!$offset) {
					break;
				}
			}
		}

		return $container;
	}
	/**
	 * @param WCDirectory $fromDir
	 * @param int $offset
	 * @return WCGContainer
	 */
	protected function getNomadsImages(WCDirectory &$fromDir, $offset) {
		$container = new WCContainer(WCImagesHolder::I());

		$stmt = null;
		$stmtID = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtID)) {
			$stmt = $this->_db->get($stmtID);
		} else {
			$query = "select  img_id\n";
			$query .= "from    {$this->_dbprefix}images\n";
			$query .= "where   img_group     = :group\n";
			$query .= " and    img_directory = :directory\n";
			$query .= " and    img_lost      = 'N'\n";
			$query .= "order by img_added asc\n";

			$stmt = $this->_db->prepare($stmtID, $query);
		}

		$params = array(
			":group" => $fromDir->group()->id,
			":directory" => $fromDir->id
		);
		if($stmt->execute($params)) {
			foreach($stmt->fetchAll() as $row) {
				$container->addId($row["img_id"]);
				$offset--;
				if(!$offset) {
					break;
				}
			}
		}

		return $container;
	}
	protected function loadOptions() {
		if(!$this->_optionsLoaded) {
			global $wcOptions;

			if($wcOptions->addValueOption("Group")) {
				$wcOptions->addOptionTrigger("Group", "g");
				$wcOptions->addOptionTrigger("Group", "group");
			}
			if($wcOptions->addOption("OnlyComics")) {
				$wcOptions->addOptionTrigger("OnlyComics", "c");
				$wcOptions->addOptionTrigger("OnlyComics", "only-comics");
			}
			if($wcOptions->addOption("OnlyImages")) {
				$wcOptions->addOptionTrigger("OnlyImages", "i");
				$wcOptions->addOptionTrigger("OnlyImages", "only-images");
			}

			$wcOptions->check();

			$this->_optionsLoaded = true;
		}
	}
	protected function loadPreRepo(WCGroup &$group, $spacer) {
		if($this->_repo && $this->_preRepo) {
			echo "{$spacer}Loading new items in '{$this->_preRepo}': ";
			//
			// @{
			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__;
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "insert ignore\n";
				$query.= "        into {$this->_dbprefix}prerepo(pre_group, pre_type, pre_id)\n";
				$query.= "        (\n";
				$query.= "                select  img_group,\n";
				$query.= "                        'IM',\n";
				$query.= "                        img_id\n";
				$query.= "                from    {$this->_dbprefix}images\n";
				$query.= "                where   img_group     = :group\n";
				$query.= "                 and    img_directory = :directory\n";
				$query.= "                 and    img_lost      = 'N'\n";
				$query.= "        ) union all (\n";
				$query.= "                select  cmc_group,\n";
				$query.= "                        'CX',\n";
				$query.= "                        cmc_id\n";
				$query.= "                from    {$this->_dbprefix}comics\n";
				$query.= "                where   cmc_group     = :group\n";
				$query.= "                 and    cmc_directory = :directory\n";
				$query.= "                 and    cmc_lost      = 'N'\n";
				$query.= "        )\n";

				$stmt = $this->_db->prepare($stmtId, $query);
			}
			// @}

			$stmt->execute(array(
				":group" => $group->id,
				":directory" => $this->_preRepo->id
			));
			echo WCColorText::ShellGreen("{$stmt->rowCount()}\n");
		}
	}
	protected function movePreRepo(WCGroup &$group, $spacer) {
		if($this->_repo && $this->_preRepo) {
			echo "{$spacer}Moving ready items from '{$this->_preRepo}': ";
			//
			// @{
			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__;
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "select  *\n";
				$query.= "from    {$this->_dbprefix}prerepo\n";
				$query.= "where   pre_group = :group\n";
				$query.= " and    pre_added < (now() - interval ".WC_E_REPOSITORIES_PREDIR_DURATION." day)\n";
				$query.= "order by pre_type asc\n";

				$stmt = $this->_db->prepare($stmtId, $query);
			}
			// @}
			if($stmt->execute(array(":group" => $group->id)) && $stmt->rowCount() > 0) {
				echo "\n";
				$imgHolder = WCImagesHolder::I();
				$cmcHolder = WCComicsHolder::I();
				foreach($stmt->fetchAll() as $row) {
					$item = false;
					$type = "";
					switch($row["pre_type"]) {
						case "IM":
							$item = $imgHolder->item($row["pre_id"]);
							$type = "image";
							break;
						case "CX":
							$item = $cmcHolder->item($row["pre_id"]);
							$type = "comic";
							break;
					}

					if($item && $item->ok()) {
						echo "{$spacer}\tMoving {$type} '{$item}': ";

						$item->setDirectory($this->_repo);

						echo WCColorText::ShellGreen("Done\n");
					}
				}
			} else {
				echo WCColorText::ShellYellow("Nothing to move\n");
			}
		}
	}
}
